package com.ezops.businessprocess.integration.customtype;

import java.io.IOException;

import org.activiti.engine.impl.variable.ValueFields;
import org.activiti.engine.impl.variable.VariableType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * Extension of activiti variable type for storing the data as JSON.
 * <p>
 * convert complex java object into json format.
 * 
 * @author vgaurav
 * 
 */
public class JsonDataType implements VariableType {

	private static final Logger LOG = LoggerFactory
			.getLogger(JsonDataType.class);

	private static final String SEPARATOR = "=>";
	private static final int SERIALIZED_MAX_LENGTH = 44000; // Activiti default
															// field length

	private ObjectMapper mapper = new ObjectMapper();

	@Override
	public String getTypeName() {
		return "serializeAsJson";
	}

	@Override
	public boolean isCachable() {
		return true;
	}

	@Override
	public Object getValue(ValueFields valueFields) {
		try {
			String value = valueFields.getTextValue();
			int pos = value.indexOf(SEPARATOR);
			if (pos < 1) {
				throw new IllegalArgumentException(
						"Value for serializeAsJson does not contain type indicator.");
			}
			String className = value.substring(0, pos);
			String json = value.substring(pos + SEPARATOR.length());
			ClassLoader classLoader = Thread.currentThread()
					.getContextClassLoader();
			if (null == classLoader) {
				classLoader = this.getClass().getClassLoader();
			}
			try {
				Class clazz = classLoader.loadClass(className);
				return mapper.readValue(json, clazz);
			} catch (ClassNotFoundException cnfe) {
				throw new IllegalArgumentException("Cannot find class  "
						+ className + ".", cnfe);
			}
		} catch (IOException ioe) {
			LOG.error(
					"Cannot convert JSON to object: "
							+ valueFields.getTextValue(), ioe);
			return null;
		}
	}

	@Override
	public void setValue(Object value, ValueFields valueFields) {
		if (null == value) {
			valueFields.setTextValue(""); // needed to allow removing variables
		} else {
			try {
				String serialized = value.getClass().getName() + SEPARATOR
						+ mapper.writeValueAsString(value);
				if (serialized.length() > SERIALIZED_MAX_LENGTH) {
					throw new IllegalArgumentException(
							"Serialized value for object of type "
									+ value.getClass().getName()
									+ " is too long to store as JSON object.");
				}
				valueFields.setTextValue(serialized);
			} catch (IOException ioe) {
				LOG.error("Cannot convert " + value.getClass().getName()
						+ " to (JSON) string: " + value, ioe);
			}
		}
	}

	@Override
	public boolean isAbleToStore(Object value) {
		return value instanceof Jsonable;
	}

}
